package com.github.difflib.text;

import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.github.difflib.DiffUtils;
import com.github.difflib.algorithm.myers.MyersDiffWithLinearSpace;
import com.github.difflib.patch.AbstractDelta;
import com.github.difflib.text.deltamerge.DeltaMergeUtils;
import com.github.difflib.text.deltamerge.InlineDeltaMergeInfo;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Pattern;
import org.junit.jupiter.api.Test;

public class DiffRowGeneratorTest {

		@Test
		public void testGenerator_Default() {
				String first = "anything \n \nother";
				String second = "anything\n\nother";

				DiffRowGenerator generator = DiffRowGenerator.create()
								.columnWidth(Integer.MAX_VALUE) // do not wrap
								.build();
				List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
				print(rows);

				assertEquals(3, rows.size());
		}

		/**
		 * Test of normalize method, of class StringUtils.
		 */
		@Test
		public void testNormalize_List() {
				DiffRowGenerator generator = DiffRowGenerator.create().build();
				assertEquals(
								Collections.singletonList("    test"), generator.normalizeLines(Collections.singletonList("\ttest")));
		}

		@Test
		public void testGenerator_Default2() {
				String first = "anything \n \nother";
				String second = "anything\n\nother";

				DiffRowGenerator generator = DiffRowGenerator.create()
								.columnWidth(0) // do not wrap
								.build();
				List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
				print(rows);

				assertEquals(3, rows.size());
		}

		@Test
		public void testGenerator_InlineDiff() {
				String first = "anything \n \nother";
				String second = "anything\n\nother";

				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.columnWidth(Integer.MAX_VALUE) // do not wrap
								.build();
				List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
				print(rows);

				assertEquals(3, rows.size());
				assertTrue(rows.get(0).getOldLine().indexOf("<span") > 0);
		}

		@Test
		public void testGenerator_IgnoreWhitespaces() {
				String first = "anything \n \nother\nmore lines";
				String second = "anything\n\nother\nsome more lines";

				DiffRowGenerator generator = DiffRowGenerator.create()
								.ignoreWhiteSpaces(true)
								.columnWidth(Integer.MAX_VALUE) // do not wrap
								.build();
				List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
				print(rows);

				assertEquals(4, rows.size());
				assertEquals(rows.get(0).getTag(), DiffRow.Tag.EQUAL);
				assertEquals(rows.get(1).getTag(), DiffRow.Tag.EQUAL);
				assertEquals(rows.get(2).getTag(), DiffRow.Tag.EQUAL);
				assertEquals(rows.get(3).getTag(), DiffRow.Tag.CHANGE);
		}

		private List<String> split(String content) {
				return Arrays.asList(content.split("\n"));
		}

		private void print(List<DiffRow> diffRows) {
				for (DiffRow row : diffRows) {
						System.out.println(row);
				}
		}

		@Test
		public void testGeneratorWithWordWrap() {
				String first = "anything \n \nother";
				String second = "anything\n\nother";

				DiffRowGenerator generator = DiffRowGenerator.create().columnWidth(5).build();
				List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
				print(rows);

				assertEquals(3, rows.size());
				assertEquals("[CHANGE,anyth<br/>ing ,anyth<br/>ing]", rows.get(0).toString());
				assertEquals("[CHANGE, ,]", rows.get(1).toString());
				assertEquals("[EQUAL,other,other]", rows.get(2).toString());
		}

		@Test
		public void testGeneratorWithMerge() {
				String first = "anything \n \nother";
				String second = "anything\n\nother";

				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.build();
				List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
				print(rows);

				assertEquals(3, rows.size());
				assertEquals(
								"[CHANGE,anything<span class=\"editOldInline\"> </span>,anything]",
								rows.get(0).toString());
				assertEquals(
								"[CHANGE,<span class=\"editOldInline\"> </span>,]", rows.get(1).toString());
				assertEquals("[EQUAL,other,other]", rows.get(2).toString());
		}

		@Test
		public void testGeneratorWithMerge2() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.build();
				List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("Test"), Arrays.asList("ester"));
				print(rows);

				assertEquals(1, rows.size());
				assertEquals(
								"[CHANGE,<span class=\"editOldInline\">T</span>est<span class=\"editNewInline\">er</span>,ester]",
								rows.get(0).toString());
		}

		@Test
		public void testGeneratorWithMerge3() {
				String first = "test\nanything \n \nother";
				String second = "anything\n\nother\ntest\ntest2";

				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.build();
				List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
				print(rows);

				assertEquals(6, rows.size());
				assertEquals(
								"[CHANGE,<span class=\"editOldInline\">test</span>,anything]",
								rows.get(0).toString());
				assertEquals(
								"[CHANGE,anything<span class=\"editOldInline\"> </span>,]",
								rows.get(1).toString());
				assertEquals(
								"[DELETE,<span class=\"editOldInline\"> </span>,]", rows.get(2).toString());
				assertEquals("[EQUAL,other,other]", rows.get(3).toString());
				assertEquals(
								"[INSERT,<span class=\"editNewInline\">test</span>,test]",
								rows.get(4).toString());
				assertEquals(
								"[INSERT,<span class=\"editNewInline\">test2</span>,test2]",
								rows.get(5).toString());
		}

		@Test
		public void testGeneratorWithMergeByWord4() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.inlineDiffByWord(true)
								.build();
				List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("Test"), Arrays.asList("ester"));
				print(rows);

				assertEquals(1, rows.size());
				assertEquals(
								"[CHANGE,<span class=\"editOldInline\">Test</span><span class=\"editNewInline\">ester</span>,ester]",
								rows.get(0).toString());
		}

		@Test
		public void testGeneratorWithMergeByWord5() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.inlineDiffByWord(true)
								.columnWidth(80)
								.build();
				List<DiffRow> rows =
								generator.generateDiffRows(Arrays.asList("Test feature"), Arrays.asList("ester feature best"));
				print(rows);

				assertEquals(1, rows.size());
				assertEquals(
								"[CHANGE,<span class=\"editOldInline\">Test</span><span class=\"editNewInline\">ester</span> <br/>feature<span class=\"editNewInline\"> best</span>,ester feature best]",
								rows.get(0).toString());
		}

		@Test
		public void testSplitString() {
				List<String> list =
								DiffRowGenerator.splitStringPreserveDelimiter("test,test2", DiffRowGenerator.SPLIT_BY_WORD_PATTERN);
				assertEquals(3, list.size());
				assertEquals("[test, ,, test2]", list.toString());
		}

		@Test
		public void testSplitString2() {
				List<String> list =
								DiffRowGenerator.splitStringPreserveDelimiter("test , test2", DiffRowGenerator.SPLIT_BY_WORD_PATTERN);
				System.out.println(list);
				assertEquals(5, list.size());
				assertEquals("[test,  , ,,  , test2]", list.toString());
		}

		@Test
		public void testSplitString3() {
				List<String> list =
								DiffRowGenerator.splitStringPreserveDelimiter("test,test2,", DiffRowGenerator.SPLIT_BY_WORD_PATTERN);
				System.out.println(list);
				assertEquals(4, list.size());
				assertEquals("[test, ,, test2, ,]", list.toString());
		}

		@Test
		public void testGeneratorExample1() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();
				List<DiffRow> rows = generator.generateDiffRows(
								Arrays.asList("This is a test senctence."), Arrays.asList("This is a test for diffutils."));

				System.out.println(rows.get(0).getOldLine());

				assertEquals(1, rows.size());
				assertEquals("This is a test ~senctence~**for diffutils**.", rows.get(0).getOldLine());
		}

		@Test
		public void testGeneratorExample2() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();
				List<DiffRow> rows = generator.generateDiffRows(
								Arrays.asList("This is a test senctence.", "This is the second line.", "And here is the finish."),
								Arrays.asList("This is a test for diffutils.", "This is the second line."));

				System.out.println("|original|new|");
				System.out.println("|--------|---|");
				for (DiffRow row : rows) {
						System.out.println("|" + row.getOldLine() + "|" + row.getNewLine() + "|");
				}

				assertEquals(3, rows.size());
				assertEquals("This is a test ~senctence~.", rows.get(0).getOldLine());
				assertEquals("This is a test **for diffutils**.", rows.get(0).getNewLine());
		}

		@Test
		public void testGeneratorUnchanged() {
				String first = "anything \n \nother";
				String second = "anything\n\nother";

				DiffRowGenerator generator = DiffRowGenerator.create()
								.columnWidth(5)
								.reportLinesUnchanged(true)
								.build();
				List<DiffRow> rows = generator.generateDiffRows(split(first), split(second));
				print(rows);

				assertEquals(3, rows.size());
				assertEquals("[CHANGE,anything ,anything]", rows.get(0).toString());
				assertEquals("[CHANGE, ,]", rows.get(1).toString());
				assertEquals("[EQUAL,other,other]", rows.get(2).toString());
		}

		@Test
		public void testGeneratorIssue14() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.inlineDiffBySplitter(line -> DiffRowGenerator.splitStringPreserveDelimiter(line, Pattern.compile(",")))
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();
				List<DiffRow> rows = generator.generateDiffRows(
								Arrays.asList("J. G. Feldstein, Chair"), Arrays.asList("T. P. Pastor, Chair"));

				System.out.println(rows.get(0).getOldLine());

				assertEquals(1, rows.size());
				assertEquals("~J. G. Feldstein~**T. P. Pastor**, Chair", rows.get(0).getOldLine());
		}

		@Test
		public void testGeneratorIssue15() throws IOException {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true) // show the ~ ~ and ** ** symbols on each difference
								.inlineDiffByWord(true) // show the ~ ~ and ** ** around each different word instead of each letter
								// .reportLinesUnchanged(true) //experiment
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();

				List<String> listOne = Files.lines(new File("target/test-classes/mocks/issue15_1.txt").toPath())
								.collect(toList());

				List<String> listTwo = Files.lines(new File("target/test-classes/mocks/issue15_2.txt").toPath())
								.collect(toList());

				List<DiffRow> rows = generator.generateDiffRows(listOne, listTwo);

				assertEquals(9, rows.size());

				for (DiffRow row : rows) {
						System.out.println("|" + row.getOldLine() + "| " + row.getNewLine() + " |");
						if (!row.getOldLine().startsWith("TABLE_NAME")) {
								assertTrue(row.getNewLine().startsWith("**ACTIONS_C16913**"));
								assertTrue(row.getOldLine().startsWith("~ACTIONS_C1700"));
						}
				}
		}

		@Test
		public void testGeneratorIssue22() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();
				String aa = "This is a test senctence.";
				String bb = "This is a test for diffutils.\nThis is the second line.";
				List<DiffRow> rows = generator.generateDiffRows(Arrays.asList(aa.split("\n")), Arrays.asList(bb.split("\n")));

				assertEquals(
								"[[CHANGE,This is a test ~senctence~.,This is a test **for diffutils**.], [INSERT,,**This is the second line.**]]",
								rows.toString());

				System.out.println("|original|new|");
				System.out.println("|--------|---|");
				for (DiffRow row : rows) {
						System.out.println("|" + row.getOldLine() + "|" + row.getNewLine() + "|");
				}
		}

		@Test
		public void testGeneratorIssue22_2() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();
				String aa = "This is a test for diffutils.\nThis is the second line.";
				String bb = "This is a test senctence.";
				List<DiffRow> rows = generator.generateDiffRows(Arrays.asList(aa.split("\n")), Arrays.asList(bb.split("\n")));

				assertEquals(
								"[[CHANGE,This is a test ~for diffutils~.,This is a test **senctence**.], [DELETE,~This is the second line.~,]]",
								rows.toString());
		}

		@Test
		public void testGeneratorIssue22_3() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();
				String aa = "This is a test senctence.";
				String bb = "This is a test for diffutils.\nThis is the second line.\nAnd one more.";
				List<DiffRow> rows = generator.generateDiffRows(Arrays.asList(aa.split("\n")), Arrays.asList(bb.split("\n")));

				assertEquals(
								"[[CHANGE,This is a test ~senctence~.,This is a test **for diffutils**.], [INSERT,,**This is the second line.**], [INSERT,,**And one more.**]]",
								rows.toString());
		}

		@Test
		public void testGeneratorIssue41DefaultNormalizer() {
				DiffRowGenerator generator = DiffRowGenerator.create().build();
				List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("<"), Arrays.asList("<"));
				assertEquals("[[EQUAL,&lt;,&lt;]]", rows.toString());
		}

		@Test
		public void testGeneratorIssue41UserNormalizer() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.lineNormalizer(str -> str.replace("\t", "    "))
								.build();
				List<DiffRow> rows = generator.generateDiffRows(Arrays.asList("<"), Arrays.asList("<"));
				assertEquals("[[EQUAL,<,<]]", rows.toString());
				rows = generator.generateDiffRows(Arrays.asList("\t<"), Arrays.asList("<"));
				assertEquals("[[CHANGE,    <,<]]", rows.toString());
		}

		@Test
		public void testGenerationIssue44reportLinesUnchangedProblem() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.reportLinesUnchanged(true)
								.oldTag(f -> "~~")
								.newTag(f -> "**")
								.build();
				List<DiffRow> rows =
								generator.generateDiffRows(Arrays.asList("<dt>To do</dt>"), Arrays.asList("<dt>Done</dt>"));
				assertEquals("[[CHANGE,<dt>~~T~~o~~ do~~</dt>,<dt>**D**o**ne**</dt>]]", rows.toString());
		}

		@Test
		public void testIgnoreWhitespaceIssue66() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.inlineDiffByWord(true)
								.ignoreWhiteSpaces(true)
								.mergeOriginalRevised(true)
								.oldTag(f -> "~") // introduce markdown style for strikethrough
								.newTag(f -> "**") // introduce markdown style for bold
								.build();

				// compute the differences for two test texts.
				// CHECKSTYLE:OFF
				List<DiffRow> rows =
								generator.generateDiffRows(Arrays.asList("This\tis\ta\ttest."), Arrays.asList("This is a test"));
				// CHECKSTYLE:ON

				assertEquals("This    is    a    test~.~", rows.get(0).getOldLine());
		}

		@Test
		public void testIgnoreWhitespaceIssue66_2() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.inlineDiffByWord(true)
								.ignoreWhiteSpaces(true)
								.mergeOriginalRevised(true)
								.oldTag(f -> "~") // introduce markdown style for strikethrough
								.newTag(f -> "**") // introduce markdown style for bold
								.build();

				// compute the differences for two test texts.
				List<DiffRow> rows =
								generator.generateDiffRows(Arrays.asList("This  is  a  test."), Arrays.asList("This is a test"));

				assertEquals("This  is  a  test~.~", rows.get(0).getOldLine());
		}

		@Test
		public void testIgnoreWhitespaceIssue64() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.inlineDiffByWord(true)
								.ignoreWhiteSpaces(true)
								.mergeOriginalRevised(true)
								.oldTag(f -> "~") // introduce markdown style for strikethrough
								.newTag(f -> "**") // introduce markdown style for bold
								.build();

				// compute the differences for two test texts.
				List<DiffRow> rows = generator.generateDiffRows(
								Arrays.asList("test\n\ntestline".split("\n")),
								Arrays.asList("A new text line\n\nanother one".split("\n")));

				assertThat(rows)
								.extracting(item -> item.getOldLine())
								.containsExactly("~test~**A new text line**", "", "~testline~**another one**");
		}

		@Test
		public void testReplaceDiffsIssue63() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.inlineDiffByWord(true)
								.mergeOriginalRevised(true)
								.oldTag(f -> "~") // introduce markdown style for strikethrough
								.newTag(f -> "**") // introduce markdown style for bold
								.processDiffs(str -> str.replace(" ", "/"))
								.build();

				// compute the differences for two test texts.
				List<DiffRow> rows =
								generator.generateDiffRows(Arrays.asList("This  is  a  test."), Arrays.asList("This is a test"));

				assertEquals("This~//~**/**is~//~**/**a~//~**/**test~.~", rows.get(0).getOldLine());
		}

		@Test
		public void testProblemTooManyDiffRowsIssue65() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.reportLinesUnchanged(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.mergeOriginalRevised(true)
								.inlineDiffByWord(false)
								.replaceOriginalLinefeedInChangesWithSpaces(true)
								.build();

				List<DiffRow> diffRows = generator.generateDiffRows(
								Arrays.asList("Ich möchte nicht mit einem Bot sprechen.", "Ich soll das schon wieder wiederholen?"),
								Arrays.asList(
												"Ich möchte nicht mehr mit dir sprechen. Leite mich weiter.",
												"Kannst du mich zum Kundendienst weiterleiten?"));

				print(diffRows);

				assertThat(diffRows).hasSize(2);
		}

		@Test
		public void testProblemTooManyDiffRowsIssue65_NoMerge() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.reportLinesUnchanged(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.mergeOriginalRevised(false)
								.inlineDiffByWord(false)
								.build();

				List<DiffRow> diffRows = generator.generateDiffRows(
								Arrays.asList("Ich möchte nicht mit einem Bot sprechen.", "Ich soll das schon wieder wiederholen?"),
								Arrays.asList(
												"Ich möchte nicht mehr mit dir sprechen. Leite mich weiter.",
												"Kannst du mich zum Kundendienst weiterleiten?"));

				System.out.println(diffRows);

				assertThat(diffRows).hasSize(2);
		}

		@Test
		public void testProblemTooManyDiffRowsIssue65_DiffByWord() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.reportLinesUnchanged(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.mergeOriginalRevised(true)
								.inlineDiffByWord(true)
								.build();

				List<DiffRow> diffRows = generator.generateDiffRows(
								Arrays.asList("Ich möchte nicht mit einem Bot sprechen.", "Ich soll das schon wieder wiederholen?"),
								Arrays.asList(
												"Ich möchte nicht mehr mit dir sprechen. Leite mich weiter.",
												"Kannst du mich zum Kundendienst weiterleiten?"));

				System.out.println(diffRows);

				assertThat(diffRows).hasSize(2);
		}

		@Test
		public void testProblemTooManyDiffRowsIssue65_NoInlineDiff() {
				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(false)
								.reportLinesUnchanged(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.mergeOriginalRevised(true)
								.inlineDiffByWord(false)
								.build();

				List<DiffRow> diffRows = generator.generateDiffRows(
								Arrays.asList("Ich möchte nicht mit einem Bot sprechen.", "Ich soll das schon wieder wiederholen?"),
								Arrays.asList(
												"Ich möchte nicht mehr mit dir sprechen. Leite mich weiter.",
												"Kannst du mich zum Kundendienst weiterleiten?"));

				System.out.println(diffRows);

				assertThat(diffRows).hasSize(2);
		}

		@Test
		public void testLinefeedInStandardTagsWithLineWidthIssue81() {
				List<String> original =
								Arrays.asList(("American bobtail jaguar. American bobtail bombay but turkish angora and tomcat.\n"
																+ "Russian blue leopard. Lion. Tabby scottish fold for russian blue, so savannah yet lynx. Tomcat singapura, cheetah.\n"
																+ "Bengal tiger panther but singapura but bombay munchkin for cougar.")
												.split("\n"));
				List<String> revised = Arrays.asList(("bobtail jaguar. American bobtail turkish angora and tomcat.\n"
												+ "Russian blue leopard. Lion. Tabby scottish folded for russian blue, so savannah yettie? lynx. Tomcat singapura, cheetah.\n"
												+ "Bengal tiger panther but singapura but bombay munchkin for cougar. And more.")
								.split("\n"));

				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.ignoreWhiteSpaces(true)
								.columnWidth(100)
								.build();
				List<DiffRow> deltas = generator.generateDiffRows(original, revised);

				System.out.println(deltas);
		}

		@Test
		public void testIssue86WrongInlineDiff() throws IOException {
				String original = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue_86_original.txt"))
								.collect(joining("\n"));
				String revised = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue_86_revised.txt"))
								.collect(joining("\n"));

				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();
				List<DiffRow> rows =
								generator.generateDiffRows(Arrays.asList(original.split("\n")), Arrays.asList(revised.split("\n")));

				rows.stream().filter(item -> item.getTag() != DiffRow.Tag.EQUAL).forEach(System.out::println);
		}

		@Test
		public void testCorrectChangeIssue114() throws IOException {
				List<String> original = Arrays.asList("A", "B", "C", "D", "E");
				List<String> revised = Arrays.asList("a", "C", "", "E");

				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(false)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();
				List<DiffRow> rows = generator.generateDiffRows(original, revised);

				for (DiffRow diff : rows) {
						System.out.println(diff);
				}

				assertThat(rows)
								.extracting(item -> item.getTag().name())
								.containsExactly("CHANGE", "DELETE", "EQUAL", "CHANGE", "EQUAL");
		}

		@Test
		public void testCorrectChangeIssue114_2() throws IOException {
				List<String> original = Arrays.asList("A", "B", "C", "D", "E");
				List<String> revised = Arrays.asList("a", "C", "", "E");

				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();
				List<DiffRow> rows = generator.generateDiffRows(original, revised);

				for (DiffRow diff : rows) {
						System.out.println(diff);
				}

				assertThat(rows)
								.extracting(item -> item.getTag().name())
								.containsExactly("CHANGE", "DELETE", "EQUAL", "CHANGE", "EQUAL");
				assertThat(rows.get(1).toString()).isEqualTo("[DELETE,~B~,]");
		}

		@Test
		public void testIssue119WrongContextLength() throws IOException {
				String original = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue_119_original.txt"))
								.collect(joining("\n"));
				String revised = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue_119_revised.txt"))
								.collect(joining("\n"));

				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.build();
				List<DiffRow> rows =
								generator.generateDiffRows(Arrays.asList(original.split("\n")), Arrays.asList(revised.split("\n")));

				rows.stream().filter(item -> item.getTag() != DiffRow.Tag.EQUAL).forEach(System.out::println);
		}

		@Test
		public void testIssue129WithDeltaDecompression() {
				List<String> lines1 = Arrays.asList(
								"apple1",
								"apple2",
								"apple3",
								"A man named Frankenstein abc to Switzerland for cookies!",
								"banana1",
								"banana2",
								"banana3");
				List<String> lines2 = Arrays.asList(
								"apple1",
								"apple2",
								"apple3",
								"A man named Frankenstein",
								"xyz",
								"to Switzerland for cookies!",
								"banana1",
								"banana2",
								"banana3");
				int[] entry = {1};
				String txt = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.oldTag((tag, isOpening) -> isOpening ? "==old" + tag + "==>" : "<==old==")
								.newTag((tag, isOpening) -> isOpening ? "==new" + tag + "==>" : "<==new==")
								.build()
								.generateDiffRows(lines1, lines2)
								.stream()
								.map(row -> row.getTag().toString())
								.collect(joining(" "));
				//                .forEachOrdered(row -> {
				//                    System.out.printf("%4d %-8s %-80s %-80s\n", entry[0]++,
				//                            row.getTag(), row.getOldLine(), row.getNewLine());
				//                });

				assertThat(txt).isEqualTo("EQUAL EQUAL EQUAL CHANGE INSERT INSERT EQUAL EQUAL EQUAL");
		}

		@Test
		public void testIssue129SkipDeltaDecompression() {
				List<String> lines1 = Arrays.asList(
								"apple1",
								"apple2",
								"apple3",
								"A man named Frankenstein abc to Switzerland for cookies!",
								"banana1",
								"banana2",
								"banana3");
				List<String> lines2 = Arrays.asList(
								"apple1",
								"apple2",
								"apple3",
								"A man named Frankenstein",
								"xyz",
								"to Switzerland for cookies!",
								"banana1",
								"banana2",
								"banana3");
				int[] entry = {1};
				String txt = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.decompressDeltas(false)
								.oldTag((tag, isOpening) -> isOpening ? "==old" + tag + "==>" : "<==old==")
								.newTag((tag, isOpening) -> isOpening ? "==new" + tag + "==>" : "<==new==")
								.build()
								.generateDiffRows(lines1, lines2)
								.stream()
								.map(row -> row.getTag().toString())
								.collect(joining(" "));
				//                .forEachOrdered(row -> {
				//                    System.out.printf("%4d %-8s %-80s %-80s\n", entry[0]++,
				//                            row.getTag(), row.getOldLine(), row.getNewLine());
				//                });

				assertThat(txt).isEqualTo("EQUAL EQUAL EQUAL CHANGE CHANGE CHANGE EQUAL EQUAL EQUAL");
		}

		@Test
		public void testIssue129SkipWhitespaceChanges() throws IOException {
				String original = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue129_1.txt"))
								.collect(joining("\n"));
				String revised = Files.lines(Paths.get("target/test-classes/com/github/difflib/text/issue129_2.txt"))
								.collect(joining("\n"));

				DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.inlineDiffByWord(true)
								.ignoreWhiteSpaces(true)
								.oldTag((tag, isOpening) -> isOpening ? "==old" + tag + "==>" : "<==old==")
								.newTag((tag, isOpening) -> isOpening ? "==new" + tag + "==>" : "<==new==")
								.build();
				List<DiffRow> rows =
								generator.generateDiffRows(Arrays.asList(original.split("\n")), Arrays.asList(revised.split("\n")));

				assertThat(rows).hasSize(13);

				rows.stream().filter(item -> item.getTag() != DiffRow.Tag.EQUAL).forEach(System.out::println);
		}

		@Test
		public void testGeneratorWithWhitespaceDeltaMerge() {
				final DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**") //
								.lineNormalizer(StringUtils::htmlEntites) // do not replace tabs
								.inlineDeltaMerger(DiffRowGenerator.WHITESPACE_EQUALITIES_MERGER)
								.build();

				assertInlineDiffResult(generator, "No diff", "No diff", "No diff");
				assertInlineDiffResult(
								generator,
								" x whitespace before diff",
								" y whitespace before diff",
								" ~x~**y** whitespace before diff");
				assertInlineDiffResult(
								generator, "Whitespace after diff x ", "Whitespace after diff y ", "Whitespace after diff ~x~**y** ");
				assertInlineDiffResult(generator, "Diff x x between", "Diff y y between", "Diff ~x x~**y y** between");
				assertInlineDiffResult(generator, "Hello \t world", "Hi \t universe", "~Hello \t world~**Hi \t universe**");
				assertInlineDiffResult(
								generator,
								"The quick brown fox jumps over the lazy dog",
								"A lazy dog jumps over a fox",
								"~The quick brown fox ~**A lazy dog **jumps over ~the lazy dog~**a fox**");
		}

		@Test
		public void testGeneratorWithMergingDeltasForShortEqualities() {
				final Function<InlineDeltaMergeInfo, List<AbstractDelta<String>>> shortEqualitiesMerger =
								deltaMergeInfo -> DeltaMergeUtils.mergeInlineDeltas(
												deltaMergeInfo,
												equalities ->
																equalities.stream().mapToInt(String::length).sum() < 6);

				final DiffRowGenerator generator = DiffRowGenerator.create()
								.showInlineDiffs(true)
								.mergeOriginalRevised(true)
								.inlineDiffByWord(true)
								.oldTag(f -> "~")
								.newTag(f -> "**")
								.inlineDeltaMerger(shortEqualitiesMerger)
								.build();

				assertInlineDiffResult(generator, "No diff", "No diff", "No diff");
				assertInlineDiffResult(generator, "aaa bbb ccc", "xxx bbb zzz", "~aaa bbb ccc~**xxx bbb zzz**");
				assertInlineDiffResult(generator, "aaa bbbb ccc", "xxx bbbb zzz", "~aaa~**xxx** bbbb ~ccc~**zzz**");
		}

		private void assertInlineDiffResult(DiffRowGenerator generator, String original, String revised, String expected) {
				final List<DiffRow> rows = generator.generateDiffRows(Arrays.asList(original), Arrays.asList(revised));
				print(rows);

				assertEquals(1, rows.size());
				assertEquals(expected, rows.get(0).getOldLine().toString());
		}

		@Test
		public void testIssue188HangOnExamples() throws IOException, URISyntaxException {
				try (FileSystem zipFs = FileSystems.newFileSystem(
								Paths.get("target/test-classes/com/github/difflib/text/test.zip"), (ClassLoader) null); ) {
						List<String> original = Files.readAllLines(zipFs.getPath("old.html"));
						List<String> revised = Files.readAllLines(zipFs.getPath("new.html"));

						DiffRowGenerator generator = DiffRowGenerator.create()
										.lineNormalizer(line -> line)
										.showInlineDiffs(true)
										.mergeOriginalRevised(true)
										.inlineDiffByWord(true)
										.decompressDeltas(true)
										.oldTag(f -> f ? "<s style=\"background-color: #bbbbbb\">" : "</s>")
										.newTag(f -> f ? "<b style=\"background-color: #aaffaa\">" : "</b>")
										.build();

						// List<DiffRow> rows = generator.generateDiffRows(original, revised);
						List<DiffRow> rows = generator.generateDiffRows(
										original, DiffUtils.diff(original, revised, new MyersDiffWithLinearSpace<>()));

						System.out.println(rows);
				}
		}
}
